﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject;
using Ninject.Planning.Bindings;
using Ninject.Modules;

namespace CoolCode.Ioc
{
    /// <summary>
    /// 可注入的
    /// </summary>
    public interface IInjectable: IBind, ILink
    {
        void Inject();
    }

    public abstract class Injector : IInjectable, IBind, ILink
    {
        internal InnerModule innerModule = new InnerModule();

        protected Injector()
        {
        }

        public abstract void Inject();

        public void OnInject()
        {
            Inject();
        }

        #region IBind Members

        public IBind<T> Bind<T>()
        {
            Binder<T> binder = new Binder<T>();
            innerModule.AddBinding(binder.Binding);
            return binder;
        }

        #endregion

        #region ILink Members

        public IHold<I> Link<I, T>() where T : I
        {
            return Bind<I>().To<T>();
        }

        /// <summary>
        /// <example>绑定泛型：Link(typeof(IGeneric&lt;>), typeof(GenericService&lt;>)</example>
        /// </summary>
        /// <param name="IType"></param>
        /// <param name="TType"></param>
        /// <returns></returns>
        public IHold Link(Type IType, Type TType)
        {
            var binding = innerModule.Kernel.Bind(IType).To(TType);
            IHold hold = new Holder<object>(binding);
            return hold;
        }

        public IHold<I> Link<I>(Func<I> provide)
        {
            var binding = innerModule.Kernel.Bind<I>().ToMethod(context => provide());
            IHold<I> hold = new Holder<I>(binding);
            return hold;
        }

        public IHold<I> Link<I>(I value)
        {
            var binding = innerModule.Kernel.Bind<I>().ToConstant(value);
            IHold<I> hold = new Holder<I>(binding);
            return hold;
        }

        #endregion

        internal INinjectModule ToNinjectModule()
        {
            innerModule.Bind += (sender, e) =>
            {
                OnInject();
            };
            return innerModule;
        }

        internal class InnerModule : NinjectModule
        {
            public event EventHandler Bind;

            public override void Load()
            {
                if (this.Bind != null)
                {
                    this.Bind(this, new EventArgs());
                }
            }
        }
    }
}
